package com.example.kcp.message;

/**
 * @author "陈凯航"
 * @date 2024/9/23
 * @mail "mengxingcn@aliyun.com"
 */

import com.example.kcp.enums.CommandCode;
import com.example.kcp.enums.ExecutionCode;
import com.example.kcp.enums.ServiceCode;
import com.example.kcp.utils.BytesUtil;
import lombok.Getter;
import lombok.Setter;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

@Getter
@Setter
public class ProtocolMessage {
    private byte[] authorizationCode;
    private byte protocolVersion;
    private byte messageType;
    private short sequenceNumber;
    private ServiceCode serviceCode;
    private CommandCode commandCode;
    private ExecutionCode executionCode;
    private byte reserved1;
    private short dataLength;
    private byte[] reserved2;
    private byte[] data;


    public ProtocolMessage() {
        this.authorizationCode = new byte[16];
        this.protocolVersion = 0x01;
        this.messageType = 0x00; // 默认为请求报文
        this.sequenceNumber = 0;
        this.serviceCode = ServiceCode.DEFAULT;
        this.commandCode = CommandCode.READ_SINGLE_VARIABLE_VALUE; // 默认命令码
        this.executionCode = ExecutionCode.SUCCESS; // 请求报文置 0
        this.reserved1 = 0x00;
        this.dataLength = 0;
        this.reserved2 = new byte[2];
        this.data = new byte[0];
    }

    public ProtocolMessage(byte[] authorizationCode, byte protocolVersion, byte messageType, short sequenceNumber, ServiceCode serviceCode, CommandCode commandCode, ExecutionCode executionCode, byte reserved1, short dataLength, byte[] reserved2, byte[] data) {
        this.authorizationCode = authorizationCode;
        this.protocolVersion = protocolVersion;
        this.messageType = messageType;
        this.sequenceNumber = sequenceNumber;
        this.serviceCode = serviceCode;
        this.commandCode = commandCode;
        this.executionCode = executionCode;
        this.reserved1 = reserved1;
        this.dataLength = dataLength;
        this.reserved2 = reserved2;
        this.data = data;
    }

    public static ProtocolMessage of(byte[] authorizationCode, byte messageType, short sequenceNumber, CommandCode commandCode, short dataLength, byte[] data) {
        return new ProtocolMessage(authorizationCode, (byte) 0x01, messageType, sequenceNumber
                , ServiceCode.DEFAULT, commandCode, ExecutionCode.SUCCESS, (byte) 0x00, dataLength, new byte[]{0x00, 0x00}, data);
    }

    public void setAuthorizationCode(byte[] authorizationCode) {
        if (authorizationCode.length == 16) {
            this.authorizationCode = authorizationCode;
        }
    }


    public byte[] toByteArray() {
        ByteBuffer buffer = ByteBuffer.allocate(16 + 1 + 1 + 2 + 1 + 1 + 1 + 1 + 2 + 2 + data.length);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        buffer.put(authorizationCode);
        buffer.put(protocolVersion);
        buffer.put(messageType);
        buffer.putShort(sequenceNumber);
        buffer.put(serviceCode.getValue());
        buffer.put(commandCode.getValue());
        buffer.put(executionCode.getValue());
        buffer.put(reserved1);
        buffer.putShort(dataLength);
        buffer.put(reserved2);
        buffer.put(data);
        return buffer.array();
    }

    public static ProtocolMessage fromByteArray(byte[] byteArray) {
        ByteBuffer buffer = ByteBuffer.wrap(byteArray);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        ProtocolMessage message = new ProtocolMessage();

        // 直接从 ByteBuffer 中读取数据
        byte[] authorizationCode = new byte[16];
        buffer.get(authorizationCode);
        message.setAuthorizationCode(authorizationCode);

        message.setProtocolVersion(buffer.get());
        message.setMessageType(buffer.get());
        message.setSequenceNumber(buffer.getShort());
        message.setServiceCode(ServiceCode.ofByte(buffer.get()));

        message.setCommandCode(CommandCode.ofByte(buffer.get()));

        message.setExecutionCode(ExecutionCode.ofByte(buffer.get()));

        message.setReserved1(buffer.get());
        message.setDataLength(buffer.getShort());

        // 读取保留字段
        byte[] reserved2 = new byte[2];
        buffer.get(reserved2);
        message.setReserved2(reserved2);

        // 读取数据字段
        byte[] data = new byte[message.getDataLength()];
        buffer.get(data);
        message.setData(data);

        return message;
    }

    @Override
    public String toString() {
        return "ProtocolMessage{" +
                "authorizationCode=" + bytesToHex(authorizationCode) +
                ", protocolVersion=" + protocolVersion +
                ", messageType=" + messageType +
                ", sequenceNumber=" + sequenceNumber +
                ", serviceCode=" + serviceCode +
                ", commandCode=" + commandCode +
                ", executionCode=" + executionCode +
                ", reserved1=" + reserved1 +
                ", dataLength=" + dataLength +
                ", reserved2=" + bytesToHex(reserved2) +
                ", data=" + bytesToHex(data) +
                '}';
    }

    private static String bytesToHex(byte[] bytes) {
        return BytesUtil.bytesToHex(bytes);
    }
}
